import java.awt.*;
import java.awt.geom.*;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.image.BufferedImage;

/**
* Eine einfaches Beispiel, wie ein segmentiertes Bild in ein anderes transformiert
* werden kann. Es wird wie im Beispiel der Uhr die Technik des DoubeBuffering verwendet.
*
* @author Frank Klawonn
* Letzte Aenderung 31.05.2005
*
* @see TriangulatedImage
* @see BufferImageDrawer
* @see DoubeBufferingClockExample
*/
public class MorphingCandS extends TimerTask
{

  //Das Fenster, in dem alles angezeigt wird.
  private BufferedImageDrawer buffid;

  //Die Bilder, die ineinander umgewandelt werden sollten, werden auf
  //diese Groesse skaliert.
  private int width;
  private int height;

  //Die Anzahl der Zwischenschritte bei der Umwandlung.
  private int steps;

  //Das erste segmentierte Bild.
  private TriangulatedImage t1;

  //Das zweite segmentierte Bild.
  private TriangulatedImage t2;

  //Zur Speicherung/Erzeugung der Zwischenbilder
  private BufferedImage mix;

  //Eine Variable, die schrittweise von 0 bis 1 erhoeht wird und zur Berechnung
  //der Konvexkombinationen benoetigt wird.
  private double alpha;

  //Die Aenderung von alpha in jedem Schritt: deltAlpha = 1.0/steps
  private double deltaAlpha;


  /**
   * Konstruktor
   *
   * @param bid    Das Fenster, in dem gezeichnet werden soll.
   */
  MorphingCandS(BufferedImageDrawer bid)
  {
  	buffid = bid;

    width = 100;
    height = 150;

    steps = 100;

    deltaAlpha = 1.0/steps;

    //Da
    alpha = 0;

    //Dieses Klasse wird zum Laden der beiden Bilder verwendet.
    Image loadedImage;

    //Erzeugen des ersten triangulierten Bildes:
    t1 = new TriangulatedImage();

    //Groesse festlegen
    t1.bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);

    //Graphics2D-Objekt erzeugen
    Graphics2D g2dt1 = t1.bi.createGraphics();

    //Laden und auf das BufferedImage zeichnen.
    loadedImage = new javax.swing.ImageIcon("image1.jpg").getImage();
    g2dt1.drawImage(loadedImage,0,0,null);

    //Definition der Punkte fuer die Triangulierung.
    t1.tPoints = new Point2D[9];
    t1.tPoints[0] = new Point2D.Double(0,0);
    t1.tPoints[1] = new Point2D.Double(0,110);
    t1.tPoints[2] = new Point2D.Double(0,150);
    t1.tPoints[3] = new Point2D.Double(50,150);
    t1.tPoints[4] = new Point2D.Double(100,150);
    t1.tPoints[5] = new Point2D.Double(100,110);
    t1.tPoints[6] = new Point2D.Double(100,0);
    t1.tPoints[7] = new Point2D.Double(50,0);
    t1.tPoints[8] = new Point2D.Double(50,110);


    //Definition der Dreiecke.
    t1.triangles = new int[8][3];

    for (int i=0; i<7; i++)
    {
      t1.triangles[i][0] = i;
      t1.triangles[i][1] = i+1;
      t1.triangles[i][2] = 8;
    }

    t1.triangles[7][0] = 7;
    t1.triangles[7][1] = 0;
    t1.triangles[7][2] = 8;




    //Alles entsprechend fuer das zweite Bild.
    t2 = new TriangulatedImage();

    t2.bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    Graphics2D g2dt2 = t2.bi.createGraphics();

    loadedImage = new javax.swing.ImageIcon("image2.jpg").getImage();

    g2dt2.drawImage(loadedImage,0,0,null);

    t2.tPoints = new Point2D[9];
    t2.tPoints[0] = new Point2D.Double(0,0);
    t2.tPoints[1] = new Point2D.Double(0,40);
    t2.tPoints[2] = new Point2D.Double(0,150);
    t2.tPoints[3] = new Point2D.Double(55,150);
    t2.tPoints[4] = new Point2D.Double(100,150);
    t2.tPoints[5] = new Point2D.Double(100,40);
    t2.tPoints[6] = new Point2D.Double(100,0);
    t2.tPoints[7] = new Point2D.Double(55,0);
    t2.tPoints[8] = new Point2D.Double(55,40);


    //Die Nummern der Punkte, die die Dreiecke bilden muessen genauso wie
    //beim ersten Bild gewaehlt werden.
    t2.triangles = t1.triangles;

  }


  //Diese Methode wird in regelmaessigen Abstaenden aufgerufen. In ihr werden
  //jeweils das aktualisierte Bild berechnet und die Methode repaint zum
  //Zeichnen im Bildschirmfenster aufgerufen.
  public void run()
  {

    //Da die Methode immer wieder aufgerufen wird, darf nur interpoliert werden,
  	//wenn alpha zwischen 0 und 1 liegt.
    if (alpha>=0 && alpha<=1)
    {
      //Erzeugung des interpolierten Bildes
      mix = t1.mixWith(t2,alpha);

      //Zeichnen des interpolierten Bildes auf das BufferedImage.
      buffid.g2dbi.drawImage(mix,50,50,null);

      //Aufruf zur Aktualisierung des Bildschirmfensters
      buffid.repaint();
    }

    //Inkrementierung von alpha.
    alpha = alpha+deltaAlpha;

  }


  public static void main(String[] argv)
  {

    //  Breite des Fensters
    int width = 200;
    //Hoehe des Fensters
    int height = 250;

    //Gibt an (in Millisekunden), nach wie langer Zeit das Bild aktualisiert
    //werden soll.
    int delay = 50;

    //Das BufferedImage, das im Fenster jeweils gezeichnet wird.
    BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);


    //Das Fenster, in dem alles angezeigt wird.
    BufferedImageDrawer bid = new BufferedImageDrawer(bi,width,height);
    bid.setTitle("Umwandlung von Form und Farbe");

    //Die TimerTask, in der die Berechnungen zum Neuzeichnen stattfinden.
    MorphingCandS mcs = new MorphingCandS(bid);


    Timer t = new Timer();
    t.scheduleAtFixedRate(mcs,0,delay);

  }

}

